package net.sourceforge.openclixml.domcomparator;

import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;



/**
 * The DomComparator is the core of CLiXML. It compares the XPath results returned by the selector.
 * @version $Revision: 39 $
 * @author dominikjungo
 *
 * Last changed: $Date: 2008-01-22 21:38:51 +0100 (Tue, 22 Jan 2008) $
 * The License for this file can be found in doc/Licenses/license-OpenCliXML.txt
 */
public class DomComparator {
    
    /**
     * compares two dom Objects.
     * @param arg0 a dom object
     * @param arg1 a dom object
     * @return true if the two dom objects are same, false otherwise.
     */
    public final int compare(final Object arg0, final Object arg1) {
        if (arg0 instanceof String && arg1 instanceof Text) {
            return compare((String) arg0, (Text) arg1);
        } else if (arg0 instanceof String && arg1 instanceof Comment) {
            return compare((String) arg0, (Comment) arg1);
        } else if (arg0 instanceof NodeList && arg1 instanceof NodeList) {
            return compare((NodeList) arg0, (NodeList) arg1);
        }

        throw new ComparingNotSupportedException(arg0, arg1);
    }

    /**
     * Compares two Strings with each other.
     * @param arg0 a String
     * @param arg1 a String
     * @return true if the two Strings are equals, false otherwise
     */
    public final int compare(final String arg0, final String arg1) {
        return arg0.compareTo(arg1);
    }

    
    /**
     * Compares a String and a Boolean (it's String value) with each other.
     * @param arg0 a String
     * @param arg1 a Boolean
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final String arg0, final Boolean arg1) {
        return compare(arg0, arg1.toString());
    }

    /**
     * Compares a String and an Attribute (it's value) with each other.
     * @param arg0 a String 
     * @param arg1 an Attribute
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final String arg0, final Attr arg1) {
        return compare(arg0, arg1.getValue());
    }

    /**
     * Compares a String and a Text (it's content) with each other.
     * @param arg0 A String
     * @param arg1 A Text
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final String arg0, final Text arg1) {
        return compare(arg0, arg1.getTextContent());
    }

    /**
     * Compares a String and a Comment with eachother.
     * @param arg0 a String
     * @param arg1 a Comment
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final String arg0, final Comment arg1) {
        return compare(arg0, arg1.getTextContent());
    }

    /**
     * Compares two numbers (their value) with each other.
     * @param arg0 a Number
     * @param arg1 a Number
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Number arg0, final Number arg1) {
        return compare(arg0.toString(), arg1.toString());
    }

    /**
     * Compares a Number(it's value as a String) with a String.
     * @param arg0 a Number
     * @param arg1 a String
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Number arg0, final String arg1) {
        return compare(arg0.toString(), arg1);
    }

    /**
     * Compares two Booleans (their value) with each other.
     * @param arg0 a Boolean
     * @param arg1 a Boolean
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Boolean arg0, final Boolean arg1) {
        return compare(arg0.toString(), arg1.toString());
    }

    /**
     * Compares a Boolean (it's String value) with a String.
     * @param arg0 a Boolean
     * @param arg1 a String
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Boolean arg0, final String arg1) {
        return compare(arg0.toString(), arg1);
    }

    /**
     * Compares two NodeLists with each other. This is a deep compare.
     * @param arg0 a NodeList
     * @param arg1 a NodeList
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final NodeList arg0, final NodeList arg1) {
        if (arg0.getLength() != arg1.getLength()) {
            return ReturnTypes.differentNodeListLength.getCode();
        }

        for (int i = 0; i < arg0.getLength(); i++) {
            if (compare(arg0.item(i), arg1.item(i)) != 0) {
                return ReturnTypes.differentNode.getCode();
            }
        }

        return 0;
    }

    /**
     * Compares a NodeList with an Object.
     * @param arg0 a NodeList
     * @param arg1 an Object
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final NodeList arg0, final Object arg1) {
        if (arg0.getLength() != 1) {
            return ReturnTypes.differentNodeListLength.getCode();
        }

        return compare(arg0.item(0), arg1);
    }

    /**
     * Compares two Attributes (their value as a String) with each other.
     * @param arg0 an Attribute
     * @param arg1 an Attribute
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Attr arg0, final Attr arg1) {
        return compare(arg0.getValue(), arg1.getValue());
    }

    /**
     * Compares an Attribute (it's value as a String) with a String.
     * @param arg0 an Attribute
     * @param arg1 a String
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Attr arg0, final String arg1) {
        return compare(arg0.getValue(), arg1);
    }

    /**
     * Compares two CDATASections (their Text) with each other.
     * @param arg0 a CDataSection
     * @param arg1 a CDataSection
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final CDATASection arg0, final CDATASection arg1) {
        return compare(arg0.getWholeText(), arg1.getWholeText());
    }

    /**
     * Compares a CDatasection (it's Text) with a String.
     * @param arg0 a CDATASection
     * @param arg1 a String
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final CDATASection arg0, final String arg1) {
        return compare(arg0.getWholeText(), arg1);
    }

    /*public int compare(CharacterData arg0, CharacterData arg1) {
        return compare(arg0.getTextContent(), arg1.getTextContent());
    }

    public int compare(CharacterData arg0, String arg1) {
        return compare(arg0.getTextContent(), arg1);
    }*/
    
    /**
     * Compares two Comments (their content) with each other.
     * @param arg0 a Comment
     * @param arg1 a Comment
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Comment arg0, final Comment arg1) {
        return compare(arg0.getTextContent(), arg1.getTextContent());
    }

    /**
     * Compares a Comment (it's Text content) and a String.
     * @param arg0 a Comment
     * @param arg1 a String
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Comment arg0, final String arg1) {
        return compare(arg0.getTextContent(), arg1);
    }

    /**
     * Compares two documents with each other. This function does a deep compare.
     * @param arg0 a Document
     * @param arg1 a Document
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Document arg0, final Document arg1) {
        return compare(arg0.getChildNodes(), arg1.getChildNodes());
    }

    /**
     * Compares two Document Fragemnts with eachother.
     * @param arg0 a DocumentFragment
     * @param arg1 a DocumentFragment
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final DocumentFragment arg0,
        final DocumentFragment arg1) {
        return compare((Attr) arg0.getFirstChild(), (Attr) arg1.getFirstChild());
    }

    /**
     * Compares two elements with each other. This does a deep compare.
     * @param arg0 an Element
     * @param arg1 an Element
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Element arg0, final Element arg1) {
        if (!arg0.getTagName().equals(arg1.getTagName())) {
            return ReturnTypes.differentElementName.getCode();
        }

        /*if(!arg0.getNamespaceURI().equals(arg1.getNamespaceURI())){
            return -2;
        }*/
        if (arg0.getAttributes().getLength() != arg1.getAttributes().getLength()) {
            return ReturnTypes.differentNumbersOfAttributes.getCode();
        }

        for (int i = 0; i < arg0.getAttributes().getLength(); i++) {
            if (compare((Attr) arg0.getAttributes().item(i),
                        (Attr) arg1.getAttributes().item(i)) != 0) {
                return ReturnTypes.differentAttribute.getCode();
            }
        }

        if (compare(arg0.getChildNodes(), arg1.getChildNodes()) != 0) {
            return ReturnTypes.differentChild.getCode();
        }

        return ReturnTypes.same.getCode();
    }

    /*
       public int compare(Entity arg0, Entity arg1) {
           return compare(arg0.getNotationName(), arg1.getNotationName());
       }

       public int compare(Entity arg0, String arg1) {
           return compare(arg0.getNotationName(),arg1);
       }

       public int compare(EntityReference arg0, EntityReference arg1) {
           return compare(arg0.getTextContent(),arg1.getTextContent());
       }

       public int compare(EntityReference arg0, String arg1) {
           return compare(arg0.getTextContent(),arg1);
       }


       public int compare(Notation arg0, Notation arg1) {
           return compare(arg0.getTextContent(),arg1.getTextContent());
       }

       public int compare(Notation arg0, String arg1) {
           return compare(arg0.getTextContent(),arg1);
       }
     */
    
    /**
     * Compare two processing Instructions (thir Content) with each other.
     * @param arg0 a ProcessingInstruction
     * @param arg1 a ProcessingInstruction
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final ProcessingInstruction arg0,
        final ProcessingInstruction arg1) {
        return compare(arg0.getTextContent(), arg1.getTextContent());
    }

    /**
     * Compares two Texts(their content) with each other.
     * @param arg0 a Text
     * @param arg1 a Text
     * @return true if they/their contents) are equal, false otherwise.
     */
    public final int compare(final Text arg0, final Text arg1) {
        return compare(arg0.getTextContent(), arg1.getTextContent());
    }

    /**
     * Compares a Text(it's content) with a String.
     * @param arg0 a Text
     * @param arg1 a String
     * @return true if they are equal, false otherwise.
     */
    public final int compare(final Text arg0, final String arg1) {
        return compare(arg0.getTextContent(), arg1);
    }

    /**
     * Compares two nodes with each other.
     * @param arg0 a node
     * @param arg1 a node
     * @return true if they are equal, false otherwise.
     */
    private int compare(final Node arg0, final Node arg1) {
        if (arg0 instanceof Element && arg1 instanceof Element) {
            return compare((Element) arg0, (Element) arg1);
        } else if (arg0 instanceof Text && arg1 instanceof Text) {
            return compare((Text) arg0, (Text) arg1);
        } else if (arg0 instanceof Comment && arg1 instanceof Comment) {
            return compare((Comment) arg0, (Comment) arg1);
        } else if (arg0 instanceof ProcessingInstruction &&
                arg1 instanceof ProcessingInstruction) {
            return compare((ProcessingInstruction) arg0,
                (ProcessingInstruction) arg1);
        } else if (arg0 instanceof Document && arg1 instanceof Document) {
            return compare((Document) arg0, (Document) arg1);
        }

        throw new ComparingNotSupportedException(arg0, arg1);
    }
}
